/*
 * traverse.h
 *
 *  Created on: 2008-7-24
 *      Author: fanliangliang
 */

#ifndef TRAVERSE_H_
#define TRAVERSE_H_

#include "config.h"
#include <sys/stat.h>
#include <dirent.h>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <climits>
#include <limits>

BEGIN_NAMESPACE_TOXIC

class file_info {
public:
	bool is_dir() const {
		return S_ISDIR(st.st_mode);
	}

	bool is_regular() const {
		return S_ISREG(st.st_mode);
	}

	bool is_other() const {
		return !is_dir() && !is_regular();
	}

public:
	struct stat st;
	size_t depth;
	char pathname[PATH_MAX + 1];
};

/*typedef void (*traverse_proc)(const file_info&, void*);

void traverse_file(const char* pathname, traverse_proc proc,
	void* arg = 0, size_t max_depth = 0);*/

template <typename Function>
void traverse_file_implement(char* pathname, const Function& f,
	void* arg, size_t& depth, size_t max_depth) {

	if (max_depth != 0 && depth == max_depth + 1) {
		return;
	}

	file_info fi;
	memset(&fi, 0, sizeof(fi));
	if (stat(pathname, &fi.st) < 0) {
		return;
	}

	fi.depth = depth;
	strncpy(fi.pathname, pathname, PATH_MAX);

	// call f() for the directory or file
	f(fi, arg);

	if (!fi.is_dir()) {
		return;
	}

	// so process each file in the directory
	char* ptr = pathname + strlen(pathname);
	if (ptr[-1] != '/') {
		*ptr++ = '/';
		*ptr = '\0';
	}

	DIR* dp = opendir(pathname);
	if (dp == 0) {
		return;
	}

	++depth;
	struct dirent* dirp;
	while ((dirp = readdir(dp)) != 0) {
		if (strcmp(dirp->d_name, ".") == 0 ||
			strcmp(dirp->d_name, "..") == 0) {
			// ignore dot and dot-dot
			continue;
		}

		// append name after slash
		strcpy(ptr, dirp->d_name);

		// recursive
		traverse_file_implement(pathname, f, arg, depth, max_depth);
	}
	--depth;

	// erase everything from slash onwards
	ptr[-1] = '\0';
	closedir(dp);
}

template <typename Function>
void traverse_file(const char* pathname, const Function& f,
	void* arg = 0, size_t max_depth = 0) {

	char _pathname[PATH_MAX + 1] = { 0 };
	strncpy(_pathname, pathname, PATH_MAX);
	char* ptr = _pathname + strlen(_pathname) - 1;
	if (*ptr == '/' && ptr != _pathname) {
		*ptr = '\0';
	}
	size_t depth = 0;
	traverse_file_implement(_pathname, f, arg, depth, max_depth);
}

END_NAMESPACE_TOXIC

#endif /* TRAVERSE_H_ */
